# 《外星人入侵》游戏
# 1、安装pygame模块
# python -m pip install --user pygame
# 2、创建pygame窗口和响应用户输入
import sys
from time import sleep

import pygame

from settings import Settings
from ship import Ship
from alien import Alien
from game_stats import GameStatus
from button import Button


class AlienInvasion:
    """管理游戏资源和行为的类"""
    def __init__(self):
        """初始化并创建游戏资源"""
        pygame.init()
        self.settings = Settings()
        # 声明飞船
        self.ship = False
        # 初始化屏幕
        self._full_screen()
        # 初始化飞船
        self.ship = Ship(self)
        # 初始化一群外星人
        self.aliens = pygame.sprite.Group()
        self._create_fleet()
        pygame.display.set_caption("Alien Invasion")
        # 初始化统计实例
        self.stats = GameStatus(self)
        # 开始按钮
        self.play_button = Button(self, "play")

    def run_game(self):
        """开始游戏主循环"""
        while True:
            self._check_event()
            if self.stats.game_active:
                self.ship.update()
                self._update_bullets()
                self._update_aliens()
            self._update_screen()

    def _update_screen(self):
        """重绘屏幕"""
        self.screen.fill(self.settings.bg_color)
        self.ship.blitme()
        self.aliens.draw(self.screen)
        self.stats._show_status()
        if not self.stats.game_active:
            self.play_button.draw_button()
        # 让最近绘制的屏幕可见
        pygame.display.flip()

    def _check_event(self):
        """监视键盘和鼠标事件"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                self._check_keydown_events(event)
            elif event.type == pygame.KEYUP:
                self._check_keyup_events(event)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                self._check_play_button(mouse_pos)

    def _check_keydown_events(self, event):
        """响应按键"""
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = True
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = True
        elif event.key == pygame.K_UP:
            self.ship.moving_up = True
        elif event.key == pygame.K_DOWN:
            self.ship.moving_down = True
        elif event.key == pygame.K_q:
            sys.exit()
        elif event.key == pygame.K_p:
            self._start_game()
        elif event.key == pygame.K_r:
            self.stats.reset_stats()
            self._stop_game()
            self.reset_feature()
        elif event.key == pygame.K_f:
            self.settings.full_screen = not self.settings.full_screen
            self._full_screen()
        elif event.key == pygame.K_SPACE:
            if self.ship.bullets_allowed == 0:
                # 连发模式
                self.ship.shooting = True
            else:
                self.ship._shoot()

    def _check_keyup_events(self, event):
        """响应按键松开"""
        if event.key == pygame.K_RIGHT:
            self.ship.moving_right = False
        elif event.key == pygame.K_LEFT:
            self.ship.moving_left = False
        elif event.key == pygame.K_UP:
            self.ship.moving_up = False
        elif event.key == pygame.K_DOWN:
            self.ship.moving_down = False
        elif event.key == pygame.K_SPACE:
            self.ship.shooting = False
        elif event.key == pygame.K_l:
            if self.ship.bullets_allowed > 0:
                self.ship.bullets_allowed = 0
            else:
                self.ship.bullets_allowed = self.settings.bullets_allowed
        elif event.key == pygame.K_b:
            self.settings.bullet_type = not self.settings.bullet_type

    def _full_screen(self):
        """切换全屏"""
        if self.settings.full_screen:
            self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
            self.settings.screen_height = self.screen.get_rect().height
            self.settings.screen_width = self.screen.get_rect().width
        else:
            # 赋值的对象是pygame的surface对象，是屏幕的一部分，用于显示游戏元素。
            # set_mode()返回的surface表示整个游戏窗口
            self.settings.screen_height = self.settings.SCREEN_HEIGHT
            self.settings.screen_width = self.settings.SCREEN_WIDTH
            self.screen = pygame.display.set_mode(
                (self.settings.screen_width, self.settings.screen_height))
        if self.ship:
            self.ship.center_ship(self.screen.get_rect().midbottom)

    def _update_bullets(self):
        """子弹消失的情况:到达边界和击中目标"""
        for bullet in self.ship.bullets.copy():
            if bullet.rect.bottom <= 0:
                self.ship.bullets.remove(bullet)
        # 射击判定
        self._check_bullet_alien_collisions()

    def _check_bullet_alien_collisions(self):
        """碰撞检测"""
        # groupcollide(group1, group2, dokill1, dokill2, collided = None) -> Sprite_dict
        #   如果将 dokill 设置为 True，则 发生碰撞的精灵将被自动移除
        #   collided 参数是用于 计算碰撞的回调函数。如果没有指定，则每个精灵必须有一个 rect 属性
        collisions = pygame.sprite.groupcollide(self.ship.bullets, self.aliens,
                                                self.settings.bullet_type,
                                                True)
        if collisions:
            for aliens in collisions.values():
                self.stats.score += self.settings.alien_points * len(aliens)
                self.stats.scoreboard.prep_score()
                self.stats.scoreboard.check_high_score()
                self.stats.alien_number -= 1
        if not self.aliens:
            # 清空子弹，新建一批外星人
            self.ship.bullets.empty()
            self._create_fleet()
            self.settings.increase_speed()
            self.stats.level += 1
            self.stats.scoreboard.prep_level()
        if self.stats.alien_number == 0:
            self.game_win()

    def _create_fleet(self):
        """创建外星人群"""
        alien = Alien(self)
        # 返回Tuple
        alien_width, alien_height = alien.rect.size
        available_space_x = self.settings.screen_width - (2 * alien_width)
        number_aliens_x = available_space_x // (2 * alien_width)
        # 计算屏幕能容纳多少个外星人
        ship_height = self.ship.rect.height
        available_space_y = (self.settings.screen_height - (5 * alien_height) -
                             ship_height)
        number_rows = available_space_y // (2 * alien_height)
        # 创建外星人群
        for row_num in range(number_rows):
            for alien_number in range(number_aliens_x):
                self._create_aliens(alien_number, row_num)

    def _create_aliens(self, alien_number, row_num):
        """创建一个外星人并将其放入当前行"""
        alien = Alien(self)
        alien_width, alien_height = alien.rect.size
        alien.x = alien_width + 2 * alien_width * alien_number
        alien.rect.x = alien.x
        alien.rect.y = alien_height * 2 + 2 * alien_height * row_num
        self.aliens.add(alien)

    def _update_aliens(self):
        """更新外星人群中的所有外星人的位置"""
        self._check_fleet_edges()
        self.aliens.update()
        # 碰撞检测
        # 判断 某个精灵 和 指定精灵组 中的精灵的碰撞
        # spritecollide(sprite, group, dokill, collided = None) -> Sprite_list
        #   如果将 dokill 设置为 True，则 指定精灵组 中 发生碰撞的精灵将被自动移除
        #   collided 参数是用于 计算碰撞的回调函数
        #       如果没有指定，则每个精灵必须有一个 rect 属性
        #   返回 精灵组 中跟 精灵 发生碰撞的 精灵列表
        if pygame.sprite.spritecollide(self.ship, self.aliens, True):
            self._ship_hit()
        self._check_aliens_bottom()

    def _check_fleet_edges(self):
        """有外星人到达边缘时采取相应措施"""
        for alien in self.aliens.sprites():
            if alien.check_edges():
                self._change_fleet_direction()
                break

    def _change_fleet_direction(self):
        """整群外星人下移，并改变它们移动的方向"""
        for alien in self.aliens.sprites():
            alien.rect.y += self.settings.fleet_drop_speed
        self.settings.fleet_direction *= -1

    def _ship_hit(self):
        """飞船与外星人碰撞"""
        if self.stats.ship_lef > 0:
            self.stats.ship_lef -= 1
            self.stats.prep_ships()
            # 重置
            self.reset_feature()
            # 暂停
            sleep(self.settings.stop_time)
        else:
            self.reset_feature()
            self._stop_game()
            self.stats.reset_stats()

    def _check_aliens_bottom(self):
        """检查外星人是否到达屏幕底端"""
        screen_rect = self.screen.get_rect()
        for alien in self.aliens:
            if alien.rect.bottom >= screen_rect.bottom:
                self._ship_hit()
                break

    def reset_feature(self):
        """重置元素"""
        self.empty_feature()
        self._create_fleet()
        self.ship.center_ship(self.screen.get_rect().midbottom)

    def empty_feature(self):
        """清空元素"""
        self.aliens.empty()
        self.ship.bullets.empty()

    def game_win(self):
        self.stats.reset_stats()
        self._stop_game()
        self.empty_feature()

    def _check_play_button(self, mouse_pos):
        """点击play"""
        if self.play_button.rect.collidepoint(mouse_pos):
            self._start_game()

    def _start_game(self):
        self.stats.game_active = True
        # 隐藏鼠标光标
        pygame.mouse.set_visible(False)

    def _stop_game(self):
        """游戏暂停"""
        self.stats.game_active = False
        pygame.mouse.set_visible(True)


if __name__ == "__main__":
    ai = AlienInvasion()
    ai.run_game()
